#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glu32.lib")

#include <Windows.h>
#include <gl/GLee.h>
#include <gl/GL.h>
#include <gl/GLu.h>
#include "OpenGLRenderer.h"
#include "Win32Window.h"
#include "Point.h"
#include "OnWindowResizeEvent.h"

Pointer<IWindow> OpenGLRenderer::Initialize()
{
	m_window = new Win32Window();
	
	if(!m_window.isValid())
		return 0;

	m_window->Create(m_windowTitle.c_str(), m_windowWidth, m_windowHeight, false);
	
	if(!(m_windowHandle = m_window->GetHandle()))
		return 0;
	if(!CreateDeviceContext())
		return 0;
	if(!CreateRenderContext())
		return 0;

	m_window->RegisterHandler(OnWindowResizeEvent::type, new CEventHandler<OpenGLRenderer>(this, &OpenGLRenderer::ResizeWindow));
	ResizeWindow(OnWindowResizeEvent(m_window->Size()));

	glShadeModel(m_shadeModel);				// Enable Smooth Shading
	glClearColor(m_clearColor[0], m_clearColor[1], m_clearColor[2], m_clearColor[3]);			// Black Background
	glClearDepth(m_clearDepthValue);					// Depth Buffer Setup
	if (m_depthTest)
		glEnable(GL_DEPTH_TEST);				// Enables Depth Testing
	glDepthFunc(m_depthFunc);					// The Type Of Depth Testing To Do
	glHint(m_hints[0], m_hints[1]);				// Really Nice Perspective Calculations

	return m_window;
}
void OpenGLRenderer::Deinitialize()
{
	for(Shader::Map_t::iterator it = m_shaders.begin(); it != m_shaders.end(); ++it)
	{
		glDeleteProgram(it->second.m_shaderID);
	}
	m_shaders.clear();
	for(Texture::Map_t::iterator it = m_textures.begin(); it != m_textures.end(); ++it)
	{
		glDeleteTextures(1, &(it->second.m_textureID));
		it->second.m_textureID = 0;
	}
	m_textures.clear();
	for(Font::Map_t::iterator it = m_fonts.begin(); it != m_fonts.end(); ++it)
	{	
		glDeleteLists(it->second->Base(), 96);
	}
	m_fonts.clear();
	DestroyRenderContext();
	DestroyDeviceContext();
}
void* OpenGLRenderer::operator new (size_t size)
{
	if (m_Instance == 0)
		m_Instance = (IRenderer*)new unsigned char[size];
	return m_Instance;
}
OpenGLRenderer::OpenGLRenderer()
{
	m_lights = GL_LIGHT0;
	m_activeTexture = GL_TEXTURE0;
}
OpenGLRenderer::~OpenGLRenderer()
{
}

#pragma region Window_Functions
bool OpenGLRenderer::CreateDeviceContext()
{
	unsigned int PixelFormat;

	static	PIXELFORMATDESCRIPTOR pfd=				// pfd Tells Windows How We Want Things To Be
	{
		sizeof(PIXELFORMATDESCRIPTOR),				// Size Of This Pixel Format Descriptor
		1,											// Version Number
		PFD_DRAW_TO_WINDOW |						// Format Must Support Window
		PFD_SUPPORT_OPENGL |						// Format Must Support OpenGL
		PFD_DOUBLEBUFFER,							// Must Support Double Buffering
		PFD_TYPE_RGBA,								// Request An RGBA Format
		16,											// Select Our Color Depth
		0, 0, 0, 0, 0, 0,							// Color Bits Ignored
		0,											// No Alpha Buffer
		0,											// Shift Bit Ignored
		0,											// No Accumulation Buffer
		0, 0, 0, 0,									// Accumulation Bits Ignored
		16,											// 16Bit Z-Buffer (Depth Buffer)  
		0,											// No Stencil Buffer
		0,											// No Auxiliary Buffer
		PFD_MAIN_PLANE,								// Main Drawing Layer
		0,											// Reserved
		0, 0, 0										// Layer Masks Ignored
	};
	
	if (!(m_device = GetDC((HWND)m_windowHandle)))
	{
		DestroyDeviceContext();
		MessageBox(NULL,L"Can't Create A GL Device Context.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if (!(PixelFormat=ChoosePixelFormat((HDC)m_device,&pfd)))
	{
		DestroyDeviceContext();
		MessageBox(NULL,L"Can't Find A Suitable PixelFormat.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}

	if(!SetPixelFormat((HDC)m_device,PixelFormat,&pfd))
	{
		DestroyDeviceContext();
		MessageBox(NULL,L"Can't Set The PixelFormat.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}
	return true;
}
void OpenGLRenderer::DestroyDeviceContext()
{
	if (m_device && !ReleaseDC((HWND)m_windowHandle,(HDC)m_device))					// Are We Able To Release The DC
	{
		MessageBox(NULL,L"Release Device Context Failed.",L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		m_device=NULL;										// Set DC To NULL
	}
}
bool OpenGLRenderer::CreateRenderContext()
{	
	if (!(m_renderer=wglCreateContext((HDC)m_device)))				// Are We Able To Get A Rendering Context?
	{
		DestroyRenderContext();								// Reset The Display
		DestroyDeviceContext();
		MessageBox(NULL,L"Can't Create A GL Rendering Context.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;								// Return FALSE
	}
	if(!MakeContextCurrent())
		return false;
	return true;
}
void OpenGLRenderer::DestroyRenderContext()
{
	if (m_renderer)											// Do We Have A Rendering Context?
	{
		if (!wglMakeCurrent(NULL,NULL))					// Are We Able To Release The DC And RC Contexts?
		{
			MessageBox(NULL,L"Release Of DC And RC Failed.",L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}

		if (!wglDeleteContext((HGLRC)m_renderer))						// Are We Able To Delete The RC?
		{
			MessageBox(NULL,L"Release Rendering Context Failed.",L"SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
		}
		m_renderer=NULL;										// Set RC To NULL
	}
}
bool OpenGLRenderer::MakeContextCurrent()
{
	if(!wglMakeCurrent((HDC)m_device,(HGLRC)m_renderer))
	{
		DestroyDeviceContext();
		MessageBox(NULL,L"Can't Activate The GL Rendering Context.",L"ERROR",MB_OK|MB_ICONEXCLAMATION);
		return false;
	}
	return true;
}
bool OpenGLRenderer::ResizeWindow( IEvent& event )
{
	const HashedString type = event.Type();
	if(event.Type() == OnWindowResizeEvent::type)
	{
		OnWindowResizeEvent& data = static_cast<OnWindowResizeEvent&>(event);
		Maths::Point<int> size = data.Size();
		int width = size.X;
		int height = size.Y;
		if (height==0)										// Prevent A Divide By Zero By
			height = 1;										// Making Height Equal One

		glViewport(0,0,width,height);						// Reset The Current Viewport
		glMatrixMode(GL_PROJECTION);						// Select The Projection Matrix
		glLoadIdentity();									// Reset The Projection Matrix

		// Calculate The Aspect Ratio Of The Window
		gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.01f,1000.0f);
		
		glMatrixMode(GL_MODELVIEW);							// Select The Modelview Matrix

		m_windowWidth = width;
		m_windowHeight = height;
	}
	return false;											// Do not consume event
}
#pragma endregion

#pragma region Render_Functions

void OpenGLRenderer::SetColor( unsigned char r, unsigned char g, unsigned char b, unsigned char a )
{
	glColor4ub(r, g, b, a);
}

void OpenGLRenderer::SetColor( unsigned int color )
{
	glColor4ubv((unsigned char*)&color);
}
void OpenGLRenderer::SetShader( const HashedString& shader )
{
	if (m_activeShader != shader.getIdentifier())
	{
		Shader::Map_t::iterator it;
		if ((it = m_shaders.find(shader)) != m_shaders.end())
		{
				glUseProgram(it->second.m_shaderID);
				m_activeShader = shader.getIdentifier();
		}
	}
}
void OpenGLRenderer::UnsetShader()
{
	m_activeShader = 0;
	glUseProgram(0);
}
void OpenGLRenderer::PushTexture( const HashedString& texture )
{
	Texture::Map_t::iterator it;
	if ((it = m_textures.find(texture)) != m_textures.end())
	{
		glActiveTextureARB(m_activeTexture++);
		glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, it->second.m_textureID);
	}
}
void OpenGLRenderer::PopTexture()
{
	if (m_activeTexture > GL_TEXTURE0)
	{
		glActiveTextureARB(--m_activeTexture);
		glDisable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, 0);
	}
}
void OpenGLRenderer::SetMaterial( const Material& material )
{
	glMaterialfv(GL_FRONT, GL_AMBIENT, material.m_ambient);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, material.m_diffuse);
	glMaterialfv(GL_FRONT, GL_SPECULAR, material.m_specular);
	glMaterialf(GL_FRONT, GL_SHININESS, material.m_shininess);

	if(material.m_shader != HashedString("nil"))
		SetShader(material.m_shader);
	else
		UnsetShader();
	for(HashedString::Vector_t::const_iterator it = material.m_textures.begin(); it != material.m_textures.end(); ++it)
	{
		PushTexture(*it);
	}
}
void OpenGLRenderer::UnsetMaterial()
{
	while(m_activeTexture != GL_TEXTURE0)
	{
		PopTexture();
	}
	UnsetShader();
}
void OpenGLRenderer::SetUniforms( const HashedString& shaderName, const UniformCountLocationMap_t& uniforms ) const
{
	for (UniformCountLocationMap_t::const_iterator it = uniforms.begin(), end = uniforms.end();  it != end; ++it)
	{
		SetUniform(shaderName, it->first, it->second.first, (float*)(it->second.second), false);
	}
}
void OpenGLRenderer::EnableLight( const Light& light )
{
	glEnable(GL_LIGHTING);
	glEnable(m_lights);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadMatrixf(&m_modelViewStack.top()(0,0));
	glLightfv(m_lights, GL_POSITION, light.m_Position);
	glLightfv(m_lights, GL_AMBIENT, light.m_Ambient);
	glLightfv(m_lights, GL_DIFFUSE, light.m_Diffuse);
	glLightfv(m_lights, GL_SPECULAR, light.m_Specular);
//	glLightfv(m_lights, GL_SPOT_DIRECTION, light.m_SpotDirection);
//	glLightf(m_lights, GL_SPOT_EXPONENT, light.m_SpotExponent);
//	glLightf(m_lights, GL_SPOT_CUTOFF, light.m_SpotCutOff);
//	glLightf(m_lights, GL_CONSTANT_ATTENUATION, light.m_ConstAttenuation);
//	glLightf(m_lights, GL_LINEAR_ATTENUATION, light.m_LinearAttenuation);
//	glLightf(m_lights, GL_QUADRATIC_ATTENUATION, light.m_QuadraticAttenuation);
	glPopMatrix();
	++m_lights;
}
void OpenGLRenderer::DisableLight( const Light& light )
{
	--m_lights;
	glDisable(m_lights);
	if (m_lights == GL_LIGHT0)
		glDisable(GL_LIGHTING);
}
void OpenGLRenderer::SwapBuffer()
{
	// Error Handle?
	SwapBuffers((HDC)m_device);
}
void OpenGLRenderer::ClearBuffer()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
}
void OpenGLRenderer::SetUniform( const HashedString& shaderName, const HashedString& uniformName, int elements, float* data, bool transpose ) const
{
	Shader::Map_t::const_iterator it;
	if((it = m_shaders.find(shaderName)) == m_shaders.end())
		return;
	const Shader& shader = it->second;
	Shader::UniformMap_t::const_iterator uniform = shader.m_uniforms.find(uniformName);
	unsigned int location = uniform->second.first;
	unsigned int type = uniform->second.second;
	
	switch(type)
	{
	case Shader::VEC1:
		{
			glUniform1fv(location, elements, data);
			break;
		};
	case Shader::VEC2:
		{
			glUniform2fv(location, elements, data);
			break;
		};
	case Shader::VEC3:
		{
			glUniform3fv(location, elements, data);
			break;
		};
	case Shader::VEC4:
		{
			glUniform4fv(location, elements, data);
			break;
		};
	default:		// Check matrix
		{
			switch(type)
			{
			case Shader::MATRIX2:
				{
					glUniformMatrix2fv(location, elements, transpose, data);
					break;
				};
			case Shader::MATRIX3:
				{
					glUniformMatrix3fv(location, elements, transpose, data);
					break;
				};
			case Shader::MATRIX4:
				{
					glUniformMatrix4fv(location, elements, transpose, data);
					break;
				};
			};
			break;
		};
	};
}
void OpenGLRenderer::Render( const IVertexContainer& container )
{
	const bool VBO = container.IsVBO();
	const bool IBO = container.IsIBO();
	const unsigned int verticeCount = container.getVerticeCount();
	const unsigned int indiceCount = container.getIndiceCount();
	const VertexDeclaration& declaration = container.getVertexDeclaration();
	const unsigned int vertexSize = declaration.m_vertexSize;

	void* vertexPtr = container.getVertexPointer();
	void* indexPtr;
	
	glMatrixMode(GL_PROJECTION);
	glLoadMatrixf(&m_projectionStack.top()(0,0));
	glMatrixMode(GL_MODELVIEW);
	glLoadMatrixf(&m_modelViewStack.top()(0,0));

	if (VBO)
	{
		glBindBuffer(GL_ARRAY_BUFFER, container.getVBOID());
		if (IBO)
			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, container.getIBOID());
	}

	for(VertexType::Vector_t::const_iterator it = declaration.m_parameters.begin(), end = declaration.m_parameters.end(); it != end; ++it)
	{
		switch (it->m_parameter)
		{
			// Positional?
		case enumPosition:
			{
				glEnableClientState(GL_VERTEX_ARRAY);
				glVertexPointer(it->m_size, GL_FLOAT, vertexSize, ((char*)vertexPtr) + it->m_offset);
				break;
			};
			// Normal?
		case enumNormal:
			{
				glEnableClientState(GL_NORMAL_ARRAY);
				glNormalPointer(GL_FLOAT, vertexSize, ((char*)vertexPtr) + it->m_offset);
				break;
			};
			// Color?
		case enumColor:
			{
				glEnableClientState(GL_COLOR_ARRAY);
				glColorPointer(it->m_size, GL_UNSIGNED_BYTE, vertexSize, ((char*)vertexPtr) + it->m_offset);
				break;
			};
			// Everything else
		default:
			{
				switch(it->m_parameter)
				{
				case enumTexCoord0:
				case enumTexCoord1:
				case enumTexCoord2:
				case enumTexCoord3:
				case enumTexCoord4:
				case enumTexCoord5:
				case enumTexCoord6:
				case enumTexCoord7:
					{
						glClientActiveTexture(it->m_parameter);
						glEnableClientState(GL_TEXTURE_COORD_ARRAY);
						glTexCoordPointer(it->m_size, GL_FLOAT, vertexSize, ((char*)vertexPtr) + it->m_offset);
						break;
					};
				default:
					{
						switch(it->m_parameter)
						{
						case enumAttrib0:
						case enumAttrib1:
						case enumAttrib2:
						case enumAttrib3:
						case enumAttrib4:
						case enumAttrib5:
						case enumAttrib6:
						case enumAttrib7:
							{
								glEnableVertexAttribArray(container.getAttributeIndex()[it->m_parameter]);
								glVertexAttribPointer(container.getAttributeIndex()[it->m_parameter], it->m_size, GL_FLOAT, GL_FALSE, vertexSize, ((char*)vertexPtr) + it->m_offset);
								break;
							};
						};
						break;
					};
				};
				break;
			};
		};
	}


	if (IBO)
	{
		indexPtr = container.getIndexPointer();
		glDrawElements(GL_TRIANGLES, indiceCount, GL_UNSIGNED_INT, indexPtr);
	} else
	{
		glDrawArrays(GL_TRIANGLES, 0, verticeCount);
	}

	glDisableClientState(GL_VERTEX_ARRAY);
	glDisableClientState(GL_NORMAL_ARRAY);
	glDisableClientState(GL_COLOR_ARRAY);
	for (int i = GL_TEXTURE0; i < 8; ++i)
	{
		glClientActiveTexture(i);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}
	glClientActiveTexture(0);
	glDisableVertexAttribArray(0);
	glDisableVertexAttribArray(1);
	glDisableVertexAttribArray(2);
	glDisableVertexAttribArray(3);
	glDisableVertexAttribArray(4);
	glDisableVertexAttribArray(5);
	glDisableVertexAttribArray(6);
	glDisableVertexAttribArray(7);

}
#pragma endregion

#pragma region Create_Functions
#pragma region Shader_Functions
void OpenGLRenderer::CreateShaderSource( const HashedString& sourceName, std::vector<const char*> source, unsigned int shaderType )
{
	const char** sourceList = &(source[0]);
	unsigned int shaderSourceId = glCreateShader(shaderType);
	glShaderSource(shaderSourceId, source.size(), sourceList, 0);
	glCompileShader(shaderSourceId);
#ifdef _DEBUG
	int status = GL_TRUE;
	glGetShaderiv(shaderSourceId, GL_COMPILE_STATUS, &status);
	if(status == GL_FALSE)
	{
		int infologLength = 0;
		int charsWritten  = 0;
		char *infoLog;

		glGetShaderiv(shaderSourceId, GL_INFO_LOG_LENGTH,&infologLength);

		if (infologLength > 0)
		{
			infoLog = (char *)malloc(infologLength);
			glGetShaderInfoLog(shaderSourceId, infologLength, &charsWritten, infoLog);
			printf("%s\n",infoLog);
			free(infoLog);
		}

		exit(5);
	}
#endif
	m_shaderSources.insert(std::pair<HashedString, unsigned int>(sourceName, shaderSourceId));
}
void OpenGLRenderer::CreateShader( const HashedString& shaderName, std::vector<HashedString> sourceNames )
{
	unsigned int programId = glCreateProgram();

	for (std::vector<HashedString>::iterator it = sourceNames.begin(), end = sourceNames.end(); it != end; ++it)
	{
		std::map<HashedString, unsigned int>::iterator source = m_shaderSources.find(*it);
		if (source != m_shaderSources.end())
			glAttachShader(programId, source->second);
	}
	glLinkProgram(programId);

#ifdef _DEBUG
	int status = GL_TRUE;
	glGetShaderiv(programId, GL_LINK_STATUS, &status);
	if(status == GL_FALSE)
	{
		int infologLength = 0;
		int charsWritten  = 0;
		char *infoLog;

		glGetShaderiv(programId, GL_INFO_LOG_LENGTH,&infologLength);

		if (infologLength > 0)
		{
			infoLog = (char *)malloc(infologLength);
			glGetShaderInfoLog(programId, infologLength, &charsWritten, infoLog);
			printf("%s\n",infoLog);
			free(infoLog);
		}

		exit(5);
	}
#endif
}
void OpenGLRenderer::FreeShaderSource( const HashedString& sourceName )
{
	std::map<HashedString, unsigned int>::iterator source = m_shaderSources.find(sourceName);
	glDeleteShader( source->second );
	m_shaderSources.erase(source);
}
void OpenGLRenderer::FreeShader( const HashedString& shaderName )
{
	glDeleteProgram(m_shaders[shaderName].m_shaderID);
	m_shaders[shaderName].m_shaderID = 0;
}
void OpenGLRenderer::LinkUniform( const HashedString& shaderName, const HashedString& uniformName, Shader::UniformType type )
{
	Shader& shader = m_shaders[shaderName];
	glUseProgram(shader.m_shaderID);
	m_activeShader = shader.m_shaderID;
	int location = glGetUniformLocation( shader.m_shaderID, uniformName.getString().c_str() );
	if (location >= 0)
	{
		shader.m_uniforms.insert(Shader::UniformPair_t(uniformName, Shader::LocationTypePair_t(location, type)));
		m_shaderUniforms[shaderName].insert(StringLocationPair_t(uniformName, location));
	}
}
void OpenGLRenderer::LinkAttribute( const HashedString& shaderName, const HashedString& attributeName, Shader::UniformType type )
{
	Shader& shader = m_shaders[shaderName];
	glUseProgram(shader.m_shaderID);
	m_activeShader = shader.m_shaderID;
	int location = glGetAttribLocation(shader.m_shaderID, attributeName.getString().c_str());
	if(location >= 0)
	{
		shader.m_attributes.insert(Shader::UniformPair_t(attributeName, Shader::LocationTypePair_t(location, type)));
		m_shaderAttributes[shaderName].insert(StringLocationPair_t(attributeName, location));
	}
}
#pragma endregion

void OpenGLRenderer::LoadTexture(HashedString& name, Texture& texture, bool freeImage)
{
	// Build a texture unit and mipmaps
	glGenTextures( 1, &texture.m_textureID );
	glBindTexture( GL_TEXTURE_2D, texture.m_textureID );
	gluBuild2DMipmaps( GL_TEXTURE_2D, texture.m_image->getBPP() == 4 ? GL_RGBA : GL_RGB, texture.m_image->getResolution(), texture.m_image->getResolution(), texture.m_image->getBPP() == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, &(texture.m_image->getPixels()[0]) );

	// Setups up texture environments
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture.m_minFilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, texture.m_magFilter);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, texture.m_wrapS);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, texture.m_wrapT);
	// Texture envi
	glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, texture.m_envMode);
	if(texture.m_envMode == GL_COMBINE)
	{
		glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB, texture.m_combineRGB);
		glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, texture.m_sourcesRGB[0]);
		glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, texture.m_operandsRGB[0]);
		if(texture.m_sourcesRGB[1])
			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB, texture.m_sourcesRGB[1]);
		if(texture.m_operandsRGB[1])
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_RGB, texture.m_operandsRGB[1]);
		if(texture.m_combineAlpha == GL_COMBINE_ALPHA)
		{
			glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, texture.m_combineAlpha);
			glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, texture.m_sourcesAlpha[0]);
			glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, texture.m_operandsAlpha[0]);
			if(texture.m_sourcesAlpha[1])
				glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, texture.m_sourcesAlpha[1]);
			if(texture.m_operandsAlpha[1])
				glTexEnvi(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, texture.m_operandsAlpha[1]);
		}
	}
	m_textures.insert(Texture::Pair_t(name, texture));
	if (freeImage)
	{
		texture.m_image = 0;
	}
}

void OpenGLRenderer::BuildFont( const HashedString& fontName, unsigned int height, unsigned int width, int angleOfEscape, int orientAngle, unsigned int weight, bool italics, bool underline, bool strikeout)
{
	wchar_t    wszDest[256];
	MultiByteToWideChar(0,0, fontName.getString().c_str(), -1, wszDest, 256);

	HFONT	font;										// Windows Font ID
	HFONT	oldfont;									// Used For Good House Keeping

	int base = glGenLists(96);								// Storage For 96 Characters

	font = CreateFont(	-1 * (height),						// Height Of Font
						width,								// Width Of Font
						angleOfEscape,						// Angle Of Escapement
						orientAngle,						// Orientation Angle
						weight,							// Font Weight
						italics,							// Italic
						underline,							// Underline
						strikeout,							// Strikeout
						ANSI_CHARSET,						// Character Set Identifier
						OUT_TT_PRECIS,						// Output Precision
						CLIP_DEFAULT_PRECIS,				// Clipping Precision
						ANTIALIASED_QUALITY,				// Output Quality
						FF_DONTCARE|DEFAULT_PITCH,			// Family And Pitch
						wszDest);							// Font Name

	oldfont = (HFONT)SelectObject((HDC)m_device, font);			   // Selects The Font We Want
	wglUseFontBitmaps((HDC)m_device, 32, 96, base);					// Builds 96 Characters Starting At Character 32
	SelectObject((HDC)m_device, oldfont);							// Selects The Font We Want
	DeleteObject(font);		

	std::vector<int> buffer(128);
	GetCharWidth32((HDC)m_device, 0, 127, &(buffer[0]));

	Pointer<Font> f(new Font(fontName.getString(), base, height, buffer));
	m_fonts.insert(Font::Pair_t(fontName, f));
}
void OpenGLRenderer::FreeFont( const HashedString& fontName )
{
	Font::Map_t::iterator it;
	if ((it = m_fonts.find(fontName)) != m_fonts.end())
	{
		glDeleteLists(it->second->Base(), 96);
		m_fonts.erase(it);
	}
}
void OpenGLRenderer::PrintFont( const HashedString& fontName, unsigned int color, int x, int y, const char* fmt, ... )
{
	Pointer<Font> font;
	Font::Map_t::iterator it;
	if ((it = m_fonts.find(fontName)) == m_fonts.end())
		return;
	font = it->second;
	
	char		text[1024];								// Holds Our String
	va_list		ap;										// Pointer To List Of Arguments

	if (fmt == NULL)									// If There's No Text
		return;											// Do Nothing
#pragma warning (disable: 4996)
	va_start(ap, fmt);									// Parses The String For Variables
	    vsprintf(text, fmt, ap);						// And Converts Symbols To Actual Numbers
	va_end(ap);											// Results Are Stored In Text
	
	float matrix[16];

	unsigned char* c = (unsigned char*)&color;

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();	
	glLoadIdentity();
	glOrtho(0, m_window->Size().X, m_window->Size().Y, 0, 0, 10);
	glGetFloatv(GL_PROJECTION_MATRIX, matrix);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();	
	glLoadIdentity();									// Reset The Current Modelview Matrix
	glTranslatef(0.0f,0.0f,0.0f);
	glColor4ubv((unsigned char*)&color);
	glRasterPos2i(x, y + font->Height());
	glPushAttrib(GL_LIST_BIT);							// Pushes The Display List Bits
	glListBase(font->Base() - 32);						// Sets The Base Character to 32
	glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);	// Draws The Display List Text
	glColor4ub(255, 255, 255, 255);
	glPopAttrib();
	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
}
#pragma endregion